package org.wzl.service.service.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.code.kaptcha.Producer;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.wzl.service._do.UserInfo;
import org.wzl.service._do.UserLogin;
import org.wzl.service.bo.UserDetail;
import org.wzl.service.bo.redis.RedisUserBO;
import org.wzl.service.convert.UserMapperStruct;
import org.wzl.service.dto.UserLoginDTO;
import org.wzl.service.dto.UserRegisterDTO;
import org.wzl.service.exception.BusinessException;
import org.wzl.service.service.CacheService;
import org.wzl.service.service.UserInfoService;
import org.wzl.service.service.UserLoginService;
import org.wzl.service.mapper.UserLoginMapper;
import org.springframework.stereotype.Service;
import org.wzl.service.utils.JwtUtil;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.concurrent.TimeUnit;

import static org.wzl.service.constant.RedisKeyConstant.LOGIN_VERIFY_IMG_KEY;
import static org.wzl.service.constant.RedisKeyConstant.USER_LOGIN_INFO_KEY;
import static org.wzl.service.constant.UserLoginConstant.VERIFY_IMG_REDIS_KEY_TIMEOUT_SECONDS;

/**
* @author 卫志龙
* @description 针对表【t_user_login】的数据库操作Service实现
* @createDate 2024-06-26 15:01:30
*/
@Slf4j
@Service
public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin>
    implements UserLoginService {

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private Producer producer;

    @Resource
    private UserMapperStruct userMapperStruct;

    @Resource
    private UserInfoService userInfoService;

    @Resource
    private CacheService cacheService;

    public String getVerifyImg(String code) throws IOException {
        String capText = producer.createText();

        cacheService.set(LOGIN_VERIFY_IMG_KEY + code, capText, VERIFY_IMG_REDIS_KEY_TIMEOUT_SECONDS, TimeUnit.SECONDS);

        BufferedImage bi = producer.createImage(capText);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ImageIO.write(bi, "jpg", stream);
        byte[] bytes = stream.toByteArray();
        return Base64.getEncoder().encodeToString(bytes);
    }

    @Override
    public String login(UserLoginDTO userLoginDTO) {
        var usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userLoginDTO.getUsername(), userLoginDTO.getPassword());
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

        if (null == authenticate) {
            throw new BusinessException("用户名或密码错误");
        }

        UserLogin userLogin =((UserDetail)authenticate.getPrincipal()).getRedisUserBO().getUserLogin();
        UserInfo userInfo = userInfoService.getById(userLogin.getId());

        String key = USER_LOGIN_INFO_KEY + userLogin.getId();
        cacheService.set(key,
                new UserDetail(new RedisUserBO(userInfo, userLogin)),
                VERIFY_IMG_REDIS_KEY_TIMEOUT_SECONDS, TimeUnit.MINUTES);

        return JwtUtil.jwt(userLogin.getId(), userLogin.getUsername(),
                userLoginDTO.getRememberMe() ? JwtUtil.ExpirationTime.README : JwtUtil.ExpirationTime.NO_README);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(UserRegisterDTO userRegisterDTO) {
        //loginDo
        UserLogin userLoginDo = userMapperStruct.getDo(userRegisterDTO);

        String redisKey = LOGIN_VERIFY_IMG_KEY + userRegisterDTO.getCode();

        String verifyCode = cacheService.get(redisKey, new TypeReference<String>() {
        });
        if (ObjectUtils.isEmpty(verifyCode)) {
            throw new BusinessException("验证码过期");
        }

        userRegisterDTO.setVerifyCode(userRegisterDTO.getVerifyCode().toLowerCase());
        if (!verifyCode.equals(userRegisterDTO.getVerifyCode())) {
            throw new BusinessException("验证码错误");
        }

        if (count(Wrappers.<UserLogin>lambdaQuery()
                .eq(UserLogin::getUsername, userRegisterDTO.getUsername())) > 0) {
            throw new BusinessException("该用户名不可用");
        }

        cacheService.remove(redisKey);
        userLoginDo.setPassword(passwordEncoder.encode(userRegisterDTO.getPassword()));

        userLoginDo.setId(IdWorker.get32UUID());
        save(userLoginDo);

        //infoDo
        UserInfo userInfoDo = userMapperStruct.getInfoDo(userRegisterDTO);
        userInfoDo.setId(userLoginDo.getId());
        userInfoService.save(userInfoDo);
    }

    @Override
    public void logout() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetail userDetail = (UserDetail) authentication.getPrincipal();
        String id = userDetail.getRedisUserBO().getUserInfo().getId();
        String key = USER_LOGIN_INFO_KEY + id;

        if (cacheService.exists(key)) {
            cacheService.remove(key);
        }
    }

}




